{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Factory Planning I\n",
    "\n",
    "## Objective and Prerequisites\n",
    "\n",
    "Want to learn how to create an optimal production plan that will maximize your profits? In this example, we’ll teach you how to solve this classic production planning problem.\n",
    "\n",
    "More information on this type of model can be found in example # 3 of the fifth edition of Modeling Building in Mathematical Programming by H. P. Williams on pages 255 – 256 and 300 – 302.\n",
    "\n",
    "This modeling example is at the intermediate level, where we assume that you know Python and are familiar with the Gurobi Python API. In addition, you should have some knowledge about building mathematical optimization models.\n",
    "\n",
    "**Download the Repository** <br />\n",
    "You can download the repository containing this and other examples by clicking [here](https://github.com/Gurobi/modeling-examples/archive/master.zip). \n",
    "\n",
    "---\n",
    "## Problem Description\n",
    "\n",
    "A factory makes seven products (Prod 1 to Prod 7) using a range of machines including:\n",
    "\n",
    "- Four grinders\n",
    "- Two vertical drills\n",
    "- Three horizontal drills\n",
    "- One borer\n",
    "- One planer\n",
    "\n",
    "Each product has a defined profit contribution per unit sold (defined as the sales price per unit minus the cost of raw materials). In addition, the manufacturing of each product requires a certain amount of time on each machine (in hours). The contribution and manufacturing time value are shown below. A dash indicates that the manufacturing process for the given product does not require that machine.\n",
    "\n",
    "| <i></i> | PROD1 | PROD2 | PROD3 | PROD4 | PROD5 | PROD6 | PROD7 |\n",
    "| --- | --- | --- | --- | --- | --- | --- | --- |\n",
    "| Profit | 10 | 6 | 8 | 4 | 11 | 9 | 3 |\n",
    "| Grinding | 0.5 | 0.7 | - | - | 0.3 | 0.2 | 0.5 |\n",
    "| Vertical Drilling | 0.1 | 0.2 | - | 0.3 | - | 0.6 | - |\n",
    "| Horizontal Drilling | 0.2 | - | 0.8 | - | - | - | 0.6 |\n",
    "| Boring | 0.05 | 0.03 | - | 0.07 | 0.1 | - | 0.08 |\n",
    "| Planning | - | - | 0.01 | - | 0.05 | - | 0.05 |\n",
    "\n",
    "In each of the six months covered by this model, one or more of the machines is scheduled to be down for maintenance and as a result will not be available to use for production that month. The maintenance schedule is as follows:\n",
    "\n",
    "| Month | Machine |\n",
    "| --- | --- |\n",
    "| January | One grinder |\n",
    "| February | Two horizontal drills |\n",
    "| March | One borer |\n",
    "| April | One vertical drill |\n",
    "| May | One grinder and one vertical drill |\n",
    "| June | One horizontal drill |\n",
    "\n",
    "There are limitations on how many of each product can be sold in a given month. These limits are shown below:\n",
    "\n",
    "| Month | PROD1 | PROD2 | PROD3 | PROD4 | PROD5 | PROD6 | PROD7 |\n",
    "| --- | --- | --- | --- | --- | --- | --- | --- |\n",
    "| January | 500 | 1000 | 300 | 300 | 800 | 200 | 100 |\n",
    "| February | 600 | 500 | 200 | 0 | 400 | 300 | 150 |\n",
    "| March | 300 | 600 | 0 | 0 | 500 | 400 | 100 |\n",
    "| April | 200 | 300 | 400 | 500 | 200 | 0 | 100 |\n",
    "| May | 0 | 100 | 500 | 100 | 1000 | 300 | 0 |\n",
    "| June | 500 | 500 | 100 | 300 | 1100 | 500 | 60 |\n",
    "\n",
    "Up to 100 units of each product may be stored in inventory at a cost of $\\$0.50$ per unit per month. At the start of January, there is no product inventory. However, by the end of June, there should be 50 units of each product in inventory.\n",
    "\n",
    "The factory produces products six days a week using two eight-hour shifts per day. It may be assumed that each month consists of 24 working days. Also, for the purposes of this model, there are no production sequencing issues that need to be taken into account.\n",
    "\n",
    "What should the production plan look like? Also, is it possible to recommend any price increases and determine the value of acquiring any new machines?\n",
    "\n",
    "This problem is based on a larger model built for the Cornish engineering company of Holman Brothers.\n",
    "\n",
    "---\n",
    "## Model Formulation\n",
    "\n",
    "### Sets and Indices\n",
    "\n",
    "$t \\in \\text{Months}=\\{\\text{Jan},\\text{Feb},\\text{Mar},\\text{Apr},\\text{May},\\text{Jun}\\}$: Set of months.\n",
    "\n",
    "$p \\in \\text{Products}=\\{1,2,\\dots,7\\}$: Set of products.\n",
    "\n",
    "$m \\in \\text{Machines}=\\{\\text{Grinder},\\text{VertDrill},\\text{horiDrill},\\text{Borer},\\text{Planer}\\}$: Set of machines.\n",
    "\n",
    "### Parameters\n",
    "\n",
    "$\\text{hours_per_month} \\in \\mathbb{R}^+$: Time (in hours/month) available at any machine on a monthly basis. It results from multiplying the number of working days (24 days) by the number of shifts per day (2) by the duration of a shift (8 hours).\n",
    "\n",
    "$\\text{max_inventory} \\in \\mathbb{N}$: Maximum number of units of a single product type that can be stored in inventory at any given month.\n",
    "\n",
    "$\\text{holding_cost} \\in \\mathbb{R}^+$: Monthly cost (in USD/unit/month) of keeping in inventory a unit of any product type.\n",
    "\n",
    "$\\text{store_target} \\in \\mathbb{N}$: Number of units of each product type to keep in inventory at the end of the planning horizon.\n",
    "\n",
    "$\\text{profit}_p \\in \\mathbb{R}^+$: Profit (in USD/unit) of product $p$.\n",
    "\n",
    "$\\text{installed}_m \\in \\mathbb{N}$: Number of machines of type $m$ installed in the factory.\n",
    "\n",
    "$\\text{down}_{t,m} \\in \\mathbb{N}$: Number of machines of type $m$ scheduled for maintenance at month $t$.\n",
    "\n",
    "$\\text{time_req}_{m,p} \\in \\mathbb{R}^+$: Time (in hours/unit) needed on machine $m$ to manufacture one unit of product $p$.\n",
    "\n",
    "$\\text{max_sales}_{t,p} \\in \\mathbb{N}$: Maximum number of units of product $p$ that can be sold at month $t$.\n",
    "\n",
    "\n",
    "### Decision Variables\n",
    "\n",
    "$\\text{make}_{t,p} \\in \\mathbb{R}^+$: Number of units of product $p$ to manufacture at month $t$.\n",
    "\n",
    "$\\text{store}_{t,p} \\in [0, \\text{max_inventory}] \\subset \\mathbb{R}^+$: Number of units of product $p$ to store at month $t$.\n",
    "\n",
    "$\\text{sell}_{t,p} \\in [0, \\text{max_sales}_{t,p}] \\subset \\mathbb{R}^+$: Number of units of product $p$ to sell at month $t$.\n",
    "\n",
    "**Assumption:** We can produce fractional units.\n",
    "\n",
    "### Objective Function\n",
    "\n",
    "- **Profit:** Maximize the total profit (in USD) of the planning horizon.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{Maximize} \\quad Z = \\sum_{t \\in \\text{Months}}\\sum_{p \\in \\text{Products}}\n",
    "(\\text{profit}_p*\\text{make}_{t,p} - \\text{holding_cost}*\\text{store}_{t,p})\n",
    "\\tag{0}\n",
    "\\end{equation}\n",
    "\n",
    "### Constraints\n",
    "\n",
    "- **Initial Balance:** For each product $p$, the number of units produced should be equal to the number of units sold plus the number stored (in units of product).\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{make}_{\\text{Jan},p} = \\text{sell}_{\\text{Jan},p} + \\text{store}_{\\text{Jan},p} \\quad \\forall p \\in \\text{Products}\n",
    "\\tag{1}\n",
    "\\end{equation}\n",
    "\n",
    "- **Balance:** For each product $p$, the number of units produced in month $t$ and the ones previously stored should be equal to the number of units sold and stored in that month (in units of product).\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{store}_{t-1,p} + \\text{make}_{t,p} = \\text{sell}_{t,p} + \\text{store}_{t,p} \\quad \\forall (t,p) \\in \\text{Months} \\setminus \\{\\text{Jan}\\} \\times \\text{Products}\n",
    "\\tag{2}\n",
    "\\end{equation}\n",
    "\n",
    "- **Inventory Target:** The number of units of product $p$ kept in inventory at the end of the planning horizon should hit the target (in units of product).\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{store}_{\\text{Jun},p} = \\text{store_target} \\quad \\forall p \\in \\text{Products}\n",
    "\\tag{3}\n",
    "\\end{equation}\n",
    "- **Machine Capacity:** Total time used to manufacture any product at machine type $m$ cannot exceed its monthly capacity (in hours).\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{p \\in \\text{Products}}\\text{time_req}_{m,p}*\\text{make}_{t,p} \\leq \\text{hours_per_month}*(\\text{installed}_m - \\text{down}_{t,m}) \\quad \\forall (t,m) \\in \\text{Months} \\times \\text{Machines}\n",
    "\\tag{4}\n",
    "\\end{equation}\n",
    "\n",
    "---\n",
    "## Python Implementation\n",
    "\n",
    "We import the Gurobi Python Module and other Python libraries."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install gurobipy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "import gurobipy as gp\n",
    "from gurobipy import GRB\n",
    "\n",
    "# tested with Python 3.11 & Gurobi 11.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Input Data\n",
    "We define all the input data of the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Parameters\n",
    "\n",
    "products = [\"Prod1\", \"Prod2\", \"Prod3\", \"Prod4\", \"Prod5\", \"Prod6\", \"Prod7\"]\n",
    "machines = [\"grinder\", \"vertDrill\", \"horiDrill\", \"borer\", \"planer\"]\n",
    "months = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\"]\n",
    "\n",
    "profit = {\"Prod1\":10, \"Prod2\":6, \"Prod3\":8, \"Prod4\":4, \"Prod5\":11, \"Prod6\":9, \"Prod7\":3}\n",
    "\n",
    "time_req = {\n",
    "    \"grinder\": {    \"Prod1\": 0.5, \"Prod2\": 0.7, \"Prod5\": 0.3,\n",
    "                    \"Prod6\": 0.2, \"Prod7\": 0.5 },\n",
    "    \"vertDrill\": {  \"Prod1\": 0.1, \"Prod2\": 0.2, \"Prod4\": 0.3,\n",
    "                    \"Prod6\": 0.6 },\n",
    "    \"horiDrill\": {  \"Prod1\": 0.2, \"Prod3\": 0.8, \"Prod7\": 0.6 },\n",
    "    \"borer\": {      \"Prod1\": 0.05,\"Prod2\": 0.03,\"Prod4\": 0.07,\n",
    "                    \"Prod5\": 0.1, \"Prod7\": 0.08 },\n",
    "    \"planer\": {     \"Prod3\": 0.01,\"Prod5\": 0.05,\"Prod7\": 0.05 }\n",
    "}\n",
    "\n",
    "\n",
    "# number of machines down\n",
    "down = {(\"Jan\",\"grinder\"): 1, (\"Feb\", \"horiDrill\"): 2, (\"Mar\", \"borer\"): 1,\n",
    "        (\"Apr\", \"vertDrill\"): 1, (\"May\", \"grinder\"): 1, (\"May\", \"vertDrill\"): 1,\n",
    "        (\"Jun\", \"planer\"): 1, (\"Jun\", \"horiDrill\"): 1}\n",
    "\n",
    "# number of each machine available\n",
    "installed = {\"grinder\":4, \"vertDrill\":2, \"horiDrill\":3, \"borer\":1, \"planer\":1} \n",
    "\n",
    "# market limitation of sells\n",
    "max_sales = {\n",
    "    (\"Jan\", \"Prod1\") : 500,\n",
    "    (\"Jan\", \"Prod2\") : 1000,\n",
    "    (\"Jan\", \"Prod3\") : 300,\n",
    "    (\"Jan\", \"Prod4\") : 300,\n",
    "    (\"Jan\", \"Prod5\") : 800,\n",
    "    (\"Jan\", \"Prod6\") : 200,\n",
    "    (\"Jan\", \"Prod7\") : 100,\n",
    "    (\"Feb\", \"Prod1\") : 600,\n",
    "    (\"Feb\", \"Prod2\") : 500,\n",
    "    (\"Feb\", \"Prod3\") : 200,\n",
    "    (\"Feb\", \"Prod4\") : 0,\n",
    "    (\"Feb\", \"Prod5\") : 400,\n",
    "    (\"Feb\", \"Prod6\") : 300,\n",
    "    (\"Feb\", \"Prod7\") : 150,\n",
    "    (\"Mar\", \"Prod1\") : 300,\n",
    "    (\"Mar\", \"Prod2\") : 600,\n",
    "    (\"Mar\", \"Prod3\") : 0,\n",
    "    (\"Mar\", \"Prod4\") : 0,\n",
    "    (\"Mar\", \"Prod5\") : 500,\n",
    "    (\"Mar\", \"Prod6\") : 400,\n",
    "    (\"Mar\", \"Prod7\") : 100,\n",
    "    (\"Apr\", \"Prod1\") : 200,\n",
    "    (\"Apr\", \"Prod2\") : 300,\n",
    "    (\"Apr\", \"Prod3\") : 400,\n",
    "    (\"Apr\", \"Prod4\") : 500,\n",
    "    (\"Apr\", \"Prod5\") : 200,\n",
    "    (\"Apr\", \"Prod6\") : 0,\n",
    "    (\"Apr\", \"Prod7\") : 100,\n",
    "    (\"May\", \"Prod1\") : 0,\n",
    "    (\"May\", \"Prod2\") : 100,\n",
    "    (\"May\", \"Prod3\") : 500,\n",
    "    (\"May\", \"Prod4\") : 100,\n",
    "    (\"May\", \"Prod5\") : 1000,\n",
    "    (\"May\", \"Prod6\") : 300,\n",
    "    (\"May\", \"Prod7\") : 0,\n",
    "    (\"Jun\", \"Prod1\") : 500,\n",
    "    (\"Jun\", \"Prod2\") : 500,\n",
    "    (\"Jun\", \"Prod3\") : 100,\n",
    "    (\"Jun\", \"Prod4\") : 300,\n",
    "    (\"Jun\", \"Prod5\") : 1100,\n",
    "    (\"Jun\", \"Prod6\") : 500,\n",
    "    (\"Jun\", \"Prod7\") : 60,\n",
    "}\n",
    "\n",
    "holding_cost = 0.5\n",
    "max_inventory = 100\n",
    "store_target = 50\n",
    "hours_per_month = 2*8*24"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Deployment\n",
    "We create a model and the variables. For each product (seven kinds of products) and each time period (month), we will create variables for the amount of which products get manufactured, held, and sold. In each month, there is an upper limit on the amount of each product that can be sold. This is due to market limitations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using license file c:\\gurobi\\gurobi.lic\n"
     ]
    }
   ],
   "source": [
    "factory = gp.Model('Factory Planning I')\n",
    "\n",
    "make = factory.addVars(months, products, name=\"Make\") # quantity manufactured\n",
    "store = factory.addVars(months, products, ub=max_inventory, name=\"Store\") # quantity stored\n",
    "sell = factory.addVars(months, products, ub=max_sales, name=\"Sell\") # quantity sold"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we insert the constraints. The balance constraints ensure that the amount of product that is in  storage in the prior month plus the amount that gets manufactured equals the amount that is sold and held for each product in the current month. This ensures that all products in the model are manufactured in some month. The initial storage is empty."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "#1. Initial Balance\n",
    "Balance0 = factory.addConstrs((make[months[0], product] == sell[months[0], product] \n",
    "                  + store[months[0], product] for product in products), name=\"Initial_Balance\")\n",
    "    \n",
    "#2. Balance\n",
    "Balance = factory.addConstrs((store[months[months.index(month) -1], product] + \n",
    "                make[month, product] == sell[month, product] + store[month, product] \n",
    "                for product in products for month in months \n",
    "                if month != months[0]), name=\"Balance\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Inventory Target constraints force that at the end of the last month the storage contains the specified amount of each product."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "#3. Inventory Target\n",
    "TargetInv = factory.addConstrs((store[months[-1], product] == store_target for product in products),  name=\"End_Balance\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The capacity constraints ensure that, for each month, the time all products require on a certain kind of machine is less than or equal to the available hours for that type of machine in that month multiplied by the number of available machines in that period. Each product requires some machine hours on different machines. Each machine is down in one or more months due to maintenance, so the number and type of available machines varies per month. There can be multiple machines per machine type."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "#4. Machine Capacity\n",
    "\n",
    "MachineCap = factory.addConstrs((gp.quicksum(time_req[machine][product] * make[month, product]\n",
    "                             for product in time_req[machine])\n",
    "                    <= hours_per_month * (installed[machine] - down.get((month, machine), 0))\n",
    "                    for machine in machines for month in months),\n",
    "                   name = \"Capacity\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The objective is to maximize the profit of the company, which consists of\n",
    "the profit for each product minus the cost for storing the unsold products. This can be stated as:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "#0. Objective Function\n",
    "obj = gp.quicksum(profit[product] * sell[month, product] -  holding_cost * store[month, product]  \n",
    "               for month in months for product in products)\n",
    "\n",
    "factory.setObjective(obj, GRB.MAXIMIZE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we start the optimization and Gurobi finds the optimal solution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gurobi Optimizer version 9.1.0 build v9.1.0rc0 (win64)\n",
      "Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n",
      "Optimize a model with 79 rows, 126 columns and 288 nonzeros\n",
      "Model fingerprint: 0xead11e9d\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e-02, 1e+00]\n",
      "  Objective range  [5e-01, 1e+01]\n",
      "  Bounds range     [6e+01, 1e+03]\n",
      "  RHS range        [5e+01, 2e+03]\n",
      "Presolve removed 74 rows and 110 columns\n",
      "Presolve time: 0.01s\n",
      "Presolved: 5 rows, 16 columns, 21 nonzeros\n",
      "\n",
      "Iteration    Objective       Primal Inf.    Dual Inf.      Time\n",
      "       0    1.2466500e+05   3.640000e+02   0.000000e+00      0s\n",
      "Extra simplex iterations after uncrush: 2\n",
      "       4    9.3715179e+04   0.000000e+00   0.000000e+00      0s\n",
      "\n",
      "Solved in 4 iterations and 0.01 seconds\n",
      "Optimal objective  9.371517857e+04\n"
     ]
    }
   ],
   "source": [
    "factory.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Analysis\n",
    "\n",
    "The result of the optimization model shows that the maximum profit we can achieve is $\\$93,715.18$.\n",
    "Let's see the solution that achieves that optimal result.\n",
    "\n",
    "### Production Plan\n",
    "\n",
    "This plan determines the amount of each product to make at each period of the planning horizon. For example, in February we make 700 units of product Prod1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Prod1</th>\n",
       "      <th>Prod2</th>\n",
       "      <th>Prod3</th>\n",
       "      <th>Prod4</th>\n",
       "      <th>Prod5</th>\n",
       "      <th>Prod6</th>\n",
       "      <th>Prod7</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Jan</th>\n",
       "      <td>500.0</td>\n",
       "      <td>888.6</td>\n",
       "      <td>382.5</td>\n",
       "      <td>300.0</td>\n",
       "      <td>800.0</td>\n",
       "      <td>200.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Feb</th>\n",
       "      <td>700.0</td>\n",
       "      <td>600.0</td>\n",
       "      <td>117.5</td>\n",
       "      <td>0.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>250.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Mar</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>400.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Apr</th>\n",
       "      <td>200.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>400.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>200.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>May</th>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>600.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>1100.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>100.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Jun</th>\n",
       "      <td>550.0</td>\n",
       "      <td>550.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>350.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>550.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     Prod1  Prod2  Prod3  Prod4   Prod5  Prod6  Prod7\n",
       "Jan  500.0  888.6  382.5  300.0   800.0  200.0    0.0\n",
       "Feb  700.0  600.0  117.5    0.0   500.0  300.0  250.0\n",
       "Mar    0.0    0.0    0.0    0.0     0.0  400.0    0.0\n",
       "Apr  200.0  300.0  400.0  500.0   200.0    0.0  100.0\n",
       "May    0.0  100.0  600.0  100.0  1100.0  300.0  100.0\n",
       "Jun  550.0  550.0    0.0  350.0     0.0  550.0    0.0"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows = months.copy()\n",
    "columns = products.copy()\n",
    "make_plan = pd.DataFrame(columns=columns, index=rows, data=0.0)\n",
    "\n",
    "for month, product in make.keys():\n",
    "    if (abs(make[month, product].x) > 1e-6):\n",
    "        make_plan.loc[month, product] = np.round(make[month, product].x, 1)\n",
    "make_plan"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Sales Plan\n",
    "\n",
    "This plan defines the amount of each product to sell at each period of the planning horizon. For example, in February we sell 600 units of product Prod1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Prod1</th>\n",
       "      <th>Prod2</th>\n",
       "      <th>Prod3</th>\n",
       "      <th>Prod4</th>\n",
       "      <th>Prod5</th>\n",
       "      <th>Prod6</th>\n",
       "      <th>Prod7</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Jan</th>\n",
       "      <td>500.0</td>\n",
       "      <td>888.6</td>\n",
       "      <td>300.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>800.0</td>\n",
       "      <td>200.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Feb</th>\n",
       "      <td>600.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>200.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>400.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>150.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Mar</th>\n",
       "      <td>100.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>400.0</td>\n",
       "      <td>100.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Apr</th>\n",
       "      <td>200.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>400.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>200.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>May</th>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>1000.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Jun</th>\n",
       "      <td>500.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>50.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     Prod1  Prod2  Prod3  Prod4   Prod5  Prod6  Prod7\n",
       "Jan  500.0  888.6  300.0  300.0   800.0  200.0    0.0\n",
       "Feb  600.0  500.0  200.0    0.0   400.0  300.0  150.0\n",
       "Mar  100.0  100.0    0.0    0.0   100.0  400.0  100.0\n",
       "Apr  200.0  300.0  400.0  500.0   200.0    0.0  100.0\n",
       "May    0.0  100.0  500.0  100.0  1000.0  300.0    0.0\n",
       "Jun  500.0  500.0   50.0  300.0    50.0  500.0   50.0"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows = months.copy()\n",
    "columns = products.copy()\n",
    "sell_plan = pd.DataFrame(columns=columns, index=rows, data=0.0)\n",
    "\n",
    "for month, product in sell.keys():\n",
    "    if (abs(sell[month, product].x) > 1e-6):\n",
    "        sell_plan.loc[month, product] = np.round(sell[month, product].x, 1)\n",
    "sell_plan"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Inventory Plan\n",
    "\n",
    "This plan reflects the amount of product in inventory at the end of each period of the planning horizon. For example, at the end of February we have 100 units of Prod1 in inventory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Prod1</th>\n",
       "      <th>Prod2</th>\n",
       "      <th>Prod3</th>\n",
       "      <th>Prod4</th>\n",
       "      <th>Prod5</th>\n",
       "      <th>Prod6</th>\n",
       "      <th>Prod7</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Jan</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>82.5</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Feb</th>\n",
       "      <td>100.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Mar</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Apr</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>May</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>100.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Jun</th>\n",
       "      <td>50.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>50.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     Prod1  Prod2  Prod3  Prod4  Prod5  Prod6  Prod7\n",
       "Jan    0.0    0.0   82.5    0.0    0.0    0.0    0.0\n",
       "Feb  100.0  100.0    0.0    0.0  100.0    0.0  100.0\n",
       "Mar    0.0    0.0    0.0    0.0    0.0    0.0    0.0\n",
       "Apr    0.0    0.0    0.0    0.0    0.0    0.0    0.0\n",
       "May    0.0    0.0  100.0    0.0  100.0    0.0  100.0\n",
       "Jun   50.0   50.0   50.0   50.0   50.0   50.0   50.0"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows = months.copy()\n",
    "columns = products.copy()\n",
    "store_plan = pd.DataFrame(columns=columns, index=rows, data=0.0)\n",
    "\n",
    "for month, product in store.keys():\n",
    "    if (abs(store[month, product].x) > 1e-6):\n",
    "        store_plan.loc[month, product] = np.round(store[month, product].x, 1)\n",
    "store_plan"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note:** If you want to write your solution to a file, rather than print it to the terminal, you can use the model.write() command. An example implementation is:\n",
    "\n",
    "`factory.write(\"factory-planning-1-output.sol\")`\n",
    "\n",
    "---\n",
    "## References\n",
    "\n",
    "H. Paul Williams, Model Building in Mathematical Programming, fifth edition.\n",
    "\n",
    "Copyright &copy; 2020 Gurobi Optimization, LLC"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
